#include "lut.h"
#include <tr1/unordered_map>
#include <string>
#include <fstream>
#include <cassert>
#include <cstring>
#include "base/cktname.h"

using std::string;
using std::vector;
using std::endl;

static string itoa( char c, unsigned x )
{
   char buf[100];
   sprintf( buf , "%c%d" , c , x );
   return buf;
}
static bool DEL[256]={};
struct INIT{ 
   INIT()
   {
      DEL[unsigned('\t')] = DEL[unsigned(' ')] = DEL[unsigned(')')] = true;
      DEL[unsigned('(')] = DEL[unsigned('=')] = DEL [unsigned(',')] = true;
   }
}del_init;
//  del is the deliminator 
// 
static void split( const std::string & in , std::vector<std::string> & vec , bool * del )
{
   vec.clear();
   char buf[256];
   unsigned idx=0;

   for( unsigned i = 0 ; i < in.size() ; ++ i )
   {
      if( del[ (unsigned)in[i] ] )
      {
         if( idx == 0 ) continue;
         else{ 
            buf[idx] = 0 ;
            vec.push_back( buf );
            idx = 0;
         }
      }else
      {
         buf[ idx ++ ] = in[i];
      }
   }
   if( idx != 0 ){ buf[idx]=0; vec.push_back( buf ); }
}

static void bench_split( const std::string & in  , std::vector<std::string> & vec )
{
   split( in , vec , DEL );
}
static fun_t expand( fun_t x, unsigned in , unsigned until )
{
   if( in == until ) return x;
   else return expand( (x<<(1<<in))|x , in + 1 , until );
}
void FPGA::read_bench( const char * file , CktName * ckt )
{
   unsigned idx ; 
   std::tr1::unordered_map< std::string , unsigned > id;
   vector<std::string> name;
   vector<LUT>  dat;
   const unsigned PI_MARK=0xFFFFFFFF;
   LUT pi; pi[0]=PI_MARK;pi[1]=pi[2]=pi[3]=0; pi.init=0;

   // const init  const 0 and const 1
   idx = 2 ;
   id.insert( std::make_pair( "gnd" , 0 ) );
   id.insert( std::make_pair( "vdd" , 1 ) );
   name.push_back( "gnd" );
   name.push_back( "vdd" );
   dat.push_back( pi );
   dat.push_back( pi );

   std::ifstream in( file );

   /// phase 1 construct name , io number 
   string line;
   vector<string> vec;
   while( in.eof() == false )
   {
      getline( in , line );
      if( line[0] == '#' ) continue;
      bench_split( line , vec );
      if( vec.size() == 0 ) continue;
      if( vec[0] == "OUTPUT" ) continue;

      string n = (vec[0]=="INPUT")?vec[1]:vec[0];
      assert( id.find(n) == id.end() );
      id.insert( std::make_pair( n , idx ++ ) );
      name.push_back( n );
   }
   assert( idx == name.size() );

   /// phase 2 connecting 
   in.clear();
   in.seekg (0, std::ios::beg);
   vector<unsigned> poidx;
   vector<unsigned> piidx;

   while( in.eof() == false )
   {
      getline( in, line );
      if( line[0] == '#' ) continue;
      bench_split( line , vec );
      if( vec.size() == 0 ) continue;
      if( vec.size() == 2 )
      {
         if( vec[0] == "INPUT" ){
            dat.push_back( pi );
            piidx.push_back( id[ vec[1] ] );
         }else if( vec[0] == "OUTPUT" ){
            poidx.push_back( id[ vec[1] ] );
         }else
         { 
            assert( vec[1]=="gnd" || vec[1] == "vdd" ); 
            LUT node = pi ;
            if( vec[1] == "gnd" ) node.init = 0x0000;
            if( vec[1] == "vdd" ) node.init = 0xFFFF;
            node[0] = 0 ;
            dat.push_back( node );
         }
      }else
      {
         unsigned inum = vec.size() - 3 ; 
         assert( vec[1] == "LUT" );
         LUT node = pi ;
         node.init = expand( strtol( vec[2].c_str() , NULL , 0 ) , inum , 4 );
         for( unsigned i = 0 ; i < inum ; ++ i ) 
            node[i] = id[ vec[ i+3 ] ];
         dat.push_back( node );
      }
   }
   
   /// phase 3 check dag
   unsigned refcnt[ idx ]; 
   memset( refcnt , 0 , sizeof( refcnt ) );
   vector<unsigned> order;

   for( unsigned i = 2 ; i < idx ; ++i ) 
   {
      if( dat[i][0] == PI_MARK ) continue;
      for( unsigned j = 0 ; j < 4 ; ++ j ) 
         refcnt[ dat[i][j] ] += 1;
   }

   for( unsigned i = 0 ; i < poidx.size() ; ++ i )
      if( refcnt[ poidx[i] ] == 0 &&  dat[ poidx[i] ][0] != PI_MARK  )
         order.push_back( poidx[i] );

   idx = 0 ;
   while( order.size() != idx )
   {
      assert( refcnt[ order[idx] ] == 0 );
      LUT next = dat[ order[idx++] ]; 
      assert( next[0] != PI_MARK );

      for( unsigned i = 0 ; i < 4 ; ++i ) 
      {
         if( next[i] == 0 ) break;
         refcnt[ next[i] ] -= 1;
         if( refcnt[ next[i] ] == 0 )
         {
            if( dat[ next[i] ][0] != PI_MARK )// not pi 
               order.push_back( next[i] );
         }
      }
   }

   // construction 
   unsigned idxmap[ name.size() ];
   idxmap[0] = 0 , idxmap[1] = 1 ;
   push_back( pi ), push_back( pi );
   idx = 2; 

   for( unsigned i = 0 ; i < piidx.size() ; ++i )
      push_back( pi ), idxmap[ piidx[i] ] = idx ++ ;

   andFlag = idx;

   for( int i = order.size() -1 ; i >=0;--i)
   {
      unsigned k = order[i];
      idxmap[ k ] = idx++;
      LUT n = dat[ k ];
      for( unsigned j = 0 ; j < 4 ; ++ j )
         n[j] = idxmap[ n[j] ];
      push_back( n );
   }
   for( unsigned i = 0 ; i < poidx.size() ; ++ i )
      po.push_back( idxmap[ poidx[i] ] );
 
   if( ckt )
   {
      // name logs , keep the same sequence of construction of the node
      for( unsigned i = 0 ; i < piidx.size() ; ++i )
         ckt->piName.push_back( name[ piidx[i] ] );
      for( unsigned i = 0 ; i < poidx.size() ; ++i )
         ckt->poName.push_back( name[ poidx[i] ] );
      for( int i = order.size() -1 ; i >= 0 ; -- i )
         ckt->nodeName.push_back( name[ order[i] ] );
   }
}
void FPGA::write_bench( const char * file , CktName * ckt )const
{
   std::ofstream out( file );
   // name recording
   string name[ size() ] ;
   if( ckt )
   {
      assert( ckt->piName.size() +2  == andFlag  );
      assert( ckt->poName.size() == po.size() );
      name[0] = "gnd" , name[1] = "vdd";
      for( unsigned i = 0 ;i < ckt->piName.size() ; ++ i )
         name[i+2] = ckt->piName[i];
      for( unsigned i = 0 ; i < po.size() ; ++ i )
      {
         assert( name[po[i]].size() == 0  || name[ po[i] ] == ckt->poName[i] );
         name[ po[i] ] = ckt->poName[i];
      }
      if( ckt->nodeName.size() != 0 )
      {
         for( unsigned i = andFlag ; i < size() ; ++i )
         {
            assert( name[i].size() == 0  || name[i] == ckt->nodeName[i-andFlag] );
            name[i] = ckt->nodeName[i-andFlag];
         }
      }
   }
   for( unsigned i = 0 ; i < size(); ++i )
      if( name[i].size() == 0 )
         name[i] = itoa( 'x' , i );

   // input 
   for( unsigned i = 2 ; i < andFlag ; ++ i )
      out << "INPUT(" << name[i] << ")" << endl ;
   for( unsigned i = 0 ; i < po.size() ; ++ i )
      out << "OUTPUT(" << name[ po[i] ] << ")" << endl ;
   out << std::hex;
   for( unsigned i = andFlag; i < size() ; ++ i )
   {
      LUT x = at(i);
      out << name[i] << " = LUT 0x" << x.init << " ( " << \
         name[ x[0] ] << " , " << name[ x[1] ]<< " , " << \
         name[ x[2] ] << " , " << name[ x[3] ] << " ) " << endl;
   }
}

